#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <netdb.h>   /* gethostbyname() necesita esta cabecera */
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>  //CAMBIAR DONDE HAY ERRORES POR exit(-1);
#include <arpa/inet.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>



#define BACKLOG 10
#define MAXDATASIZE 100

/***Estructura por la cual mantiene comunicacion con el cliente**/
struct informacionEnvio{
	char info[MAXDATASIZE];
	char flag_env[3];
};
/**Estructura para llevar estadísticas de peticiones**/
struct informacionConsultas{
	long mtype;
	int cant_peticiones;
	int normales;
	int reversas;
	int exitosas;
	int no_exitosas;

};

struct informacionConsultas cons;


void	sig_usr(int signal);  //manejador de señales
/*Funciones para el manejo de archivo /etc/hosts**/
char * obtenerHost (char * direIP); //obtener host dada dirección IP
char * obtenerIP(char * nombreHost); //obtener dirección IP dado host
//función para inicializar el struct que lleva las estadísticas
void inicializar(struct informacionConsultas *cons);


int main (int argc, char *argv[]){
	int fd; //file descriptor
	int num_puerto, sin_size,cod;
	struct sockaddr_in servidor;  
	struct sockaddr_in cliente;
	char buf[50];
	char flag_envio[3];
	
	/*variables mensaje*/
	int msqid;
	int msgflg = IPC_CREAT | 0666;
	key_t key;
	pid_t pid1;
	int estado1;


	inicializar (&cons);

	//informacion de la direccion del servidor
	if(argc!=2){
		printf("No ha ingresado el numero de puerto\n");
		return 0;
	}
	num_puerto=atoi(argv[1]);
	
	//creación de la llamada a socket()
	if ((fd=socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {  
      		printf("error en socket()\n");
     		return 0;
   	}
	memset(&servidor,0,sizeof(servidor));
	servidor.sin_family=AF_INET;
	servidor.sin_port=htons(num_puerto);
	
	inet_aton("127.0.0.1",&servidor.sin_addr);
	
	//A continuación la llamada a bind()
	if(bind(fd,(struct sockaddr*)&servidor, sizeof(struct sockaddr))==-1) {
		printf("Puerto TCP no disponible.\n");
     		exit(-1);
  	}
	if(listen(fd, BACKLOG)==-1){
		perror("Listen()");
		exit(-1);
	}
	
			
	if(signal(SIGUSR1, sig_usr) == SIG_ERR){
			printf("Error en señal\n");
			return 0;
		}
	else
		signal(SIGUSR1,sig_usr);
	if(signal(SIGINT,sig_usr)==SIG_ERR){
		printf("Error en señal\n");
		return 0;
	}	
	else
		signal(SIGINT,sig_usr);		
	/*****CREACION COLA DE MENSAJE*****/
	key=1234;


	if ((msqid = msgget(key, msgflg)) < 0)
	{
		perror("msgget");
		exit(1);
	}
	
	/****FIN CREACION COLA DE MENSAJE****/
	

	while(1) {
		
				
		sin_size=sizeof(struct sockaddr_in);
     		int fd2;//segundo file descriptor para manejarlo a través de otro proceso
		/* A continuación la llamada a accept() */
     		if ((fd2 = accept(fd,(struct sockaddr *)&cliente, &sin_size))==-1) {
        		printf("error en accept()\n");
        		return 0;
     		}
		/*envio la estructura de estadisticas al proceso hijo*/
		if (msgsnd(msqid, &cons, sizeof(cons), IPC_NOWAIT) < 0)
		{
			perror("msgsnd");
			exit(1);
		}
		if(!(pid1=fork())){
			char envioHosts[MAXDATASIZE];
			char comparacion[MAXDATASIZE]="0";
			struct informacionEnvio inf;
		
			int msqid;
			key_t key;
			key=1234;	
			
			if ((msqid = msgget(key, 0666)) < 0)
			{
				perror("msgget");
				exit(1);
			}
			/*Recibo la estructura del padre, para modificarla */
			if (msgrcv(msqid, &cons, sizeof(cons), 1, 0) < 0)
			{
				perror("msgrcv");
				exit(1);
			}
			
			//leo la informacion que me pasó el cliente
			cod=read(fd2,&inf,sizeof(struct informacionEnvio));
			
			int i;
			i=atoi(inf.flag_env);
			

			cons.cant_peticiones++;
			switch(i){
//CONSULTA REVERSA
			case 0:
				strcpy(envioHosts,obtenerIP(inf.info));
				cons.reversas++;
				//si la función obtenerIP, envió una cadena cero, es porq la consulta no ha sido exitosa
				if(!strcmp(envioHosts,comparacion)){
					cons.no_exitosas++;
				}
				else
					cons.exitosas++;
				break;
//CONSULTA NORMAL			
			case 1:
				
				strcpy(envioHosts,obtenerHost(inf.info));
				//si la función obtenerHost, envió una cadena cero, es porq la consulta no ha sido exitosa
				if(!strcmp(envioHosts,comparacion)){
					cons.no_exitosas++;
				}
				else
					cons.exitosas++;
				cons.normales++;
				break;
		
			}
			
			cod=write(fd2,envioHosts,sizeof(envioHosts));
			if(cod<0)
				perror("No he podido escribir el mensaje");
			//Cierro la conección 
			close(fd2);
			/*Envio la estructura modificada dentro del switch*/
			if (msgsnd(msqid, &cons, sizeof(cons), IPC_NOWAIT) < 0)
			{
				perror("msgsnd");
				exit(1);
			}

			exit(0);

		}
		else{
			/* Se suspende la ejecucion del proceso padre hasta que finalice el proceso hijo */
			waitpid(pid1, &estado1, 0);
			close(fd2); /* cierra fd2 */
			/*Recibo la estructura que me modificó el hijo*/
			if (msgrcv(msqid, &cons, sizeof(cons), 1, 0) < 0)
			{
				perror("msgrcv");
				exit(1);
			}			
		}
	}//fin while

	//ELIMINO COLA DE MENSAJE
	if (msgctl(msqid, IPC_RMID, NULL) == -1) {
		perror("msgctl");
		exit(1);
	}
	return 0;


}
void sig_usr(int signal){
	if(signal==SIGUSR1){
		printf("Estadisticas SOA NameServer:\n");
		printf("Total recibidas: %d\n",cons.cant_peticiones);
		printf("Normales: %d\n", cons.normales);
		printf("Reversas: %d\n", cons.reversas);
		printf("Exitosas: %d\n", cons.exitosas);
		printf("No exitosas: %d\n\n", cons.no_exitosas);
	}
	else if(signal==SIGINT){
		exit(0);
	}
	else
		printf("Señal mandada de forma erronea\n\n");
	exit(0);
}

char * obtenerHost (char * direIP){
	struct hostent *hp;
	char **p;
	u_long addr;
	if ((addr=inet_addr(direIP)) == -1 )
	{
		/*Validacion dirección IP*/		
		//printf("La direccion IP tiene que estar en notacion a.b.c.d\n");
	
		return "0";
	}

	hp=gethostbyaddr( (char *)&addr, sizeof(addr),AF_INET);
	if (hp==NULL)
	{
		//printf("No encuentro la informacion sobre la maquina %s\n", direIP);
	
		return "0";
	}
	/*Para cada uno de las posibles entradas en h_addr_list*/
	for (p=hp->h_addr_list; *p != 0; p++)
	{
		struct in_addr in;
		/*Pasa el binario de la tabla a in.s_addr porque esa estructura la */
		
		memcpy(&in.s_addr, *p, sizeof(in.s_addr));
		//printf("%s \n", hp->h_name);
	}
	
	return hp->h_name;
	
}
char * obtenerIP(char * nombreHost){
	struct hostent *p = gethostbyname(nombreHost);

 	if(p==NULL){
		//No encuentra
 		//printf("NO SE PUEDE\n");
	
  		return "0";
 	}
	

 	//printf("%s\n",inet_ntoa( *((struct in_addr *)p->h_addr)));
 	
 
	return inet_ntoa( *((struct in_addr *)p->h_addr));


}
void inicializar(struct informacionConsultas * cons){	
	cons->mtype=1;
	cons->cant_peticiones=0;
	cons->normales=0;
	cons->reversas=0;
	cons->exitosas=0;
	cons->no_exitosas=0;

}
